package cn.caplike.demo.netty.socket2;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * Description:
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2021-11-27 20:06
 */
public class MyChatServerChannelInboundHandler extends SimpleChannelInboundHandler<String> {

    /**
     * 持有 Channel 对象
     */
    private static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 3.
     * @param ctx           the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
     *                      belongs to
     * @param msg           the message to handle
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) {
        // 服务器收到任何一个客户端发来的消息之后, channelRead0 就会得到调用
        final Channel channel = ctx.channel();

        // 广播给所有 Client
        channelGroup.forEach(ch -> {
            if (channel != ch) {
                ch.writeAndFlush(channel.remoteAddress() + " 发送的消息: " + msg + "\n");
            } else {
                ch.writeAndFlush("[自己] " + msg + "\n");
            }
        });
    }

    // 1. 客户端连上netty服务器后，马上调用handlerAdded方法完成channel的添加操作(所谓channel可以理解为一个客户端)
    // 2. 添加操作执行完成以后立马调用channelRegistered方法将channel注入到netty中管理起来
    // 3. 注册好以后调用服务器端的channelActive方法，让其处于激活状态
    // 4. 调用channelRead0方法完成客户端数据的读取和相应
    // 5. 调用完成以后curl主动断开服务器的链接，并通知服务器端，服务器端就会调用channelInactive方法处理回调事件
    // 6. 最后从netty的注册中将该channel删除掉
    // 这就是netty执行回调的所有流程，特别注意的是该处我们使用的测试工具是curl，那么我们使用浏览器再次测试，会发现结果有所不同：
    // 所谓的不同不是指顺序这个顺序是多线程控制，不能作为依据，所谓的不同指的是该处没有:
    // channel inactive
    // channel unregistered
    // 如果你真的按照笔记去做测试，那么还会出现一个有意思的事情，就是过了一段时间以后他们又出来了或者你不凑巧关了浏览器他也打印出来了。
    // 那么是为什么呢？
    // curl作为一个命令行工具他发送一个命令然后得到结果以后就会自己主动的关闭连接，
    // 而浏览器作为一个http请求的发送者他不会自己主动的关闭连接，如果浏览器使用的http1.0的协议，那么当服务器端响应完成以后就会由服务器端主动关闭连接，如果浏览器使用的是http1.1，那么他就会出现我们该处出现的情况，
    // 究其原因是因为1.1的协议是支持keepAlive的,alive的时间会有一个默认值当然也可以自己去设置,那么服务器处理完该请求以后会默认等待keepAlive的时间，如果过了这个时间还没有请求过来，
    // 那么会由服务器主动断开该连接，而自己关闭浏览器属于客户端自己关闭连接，他会通知服务器，也会调用对应的时间

    /**
     * 1. 连接建立
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        final Channel channel = ctx.channel();
        channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 加入\n");
        channelGroup.add(channel); // 保存对客户端的连接
    }

    /**
     * 连接断开
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        final Channel channel = ctx.channel();
        channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 离开\n");

        // 当连接断掉后, Netty 会自动移除 channelGroup 的对应断掉的 Channel
        System.out.println("当前剩余 channel:" + channelGroup.size());
    }

    /**
     * 2.
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        final Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + " 上线");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        final Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + " 下线");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
